/*
 *
 */
package org.jiscinvolve.astrodabis.barberry.common.core;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.annotation.PostConstruct;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.HibernateException;

import org.springframework.context.ApplicationContext;

import org.springframework.stereotype.Component;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;  

import org.jiscinvolve.astrodabis.barberry.common.core.Job;
import org.jiscinvolve.astrodabis.barberry.common.core.Tag;
import org.jiscinvolve.astrodabis.barberry.common.core.Account;

import org.jiscinvolve.astrodabis.barberry.common.data.TapTable;
import org.jiscinvolve.astrodabis.barberry.common.data.TapColumn;
import org.jiscinvolve.astrodabis.barberry.common.data.TapService;

import org.jiscinvolve.astrodabis.barberry.common.data.TapRow;
import org.jiscinvolve.astrodabis.barberry.common.data.TapJoin;
import org.jiscinvolve.astrodabis.barberry.common.data.RowTag;
import org.jiscinvolve.astrodabis.barberry.common.data.JoinTag;

@Component("womble")
public class Womble
//implements InitializingBean
    {

    protected static Log log = LogFactory.getLog(Womble.class);

    public static Womble instance ;

    public static Womble womble()
        {
        //log.debug("-- WOMBLE --");
        //log.debug("Womble.womble()");
        //log.debug("Womble.instance [" + instance + "]");
        return instance ;
        }

    public static void womble(Womble womble)
        {
        log.debug("-- WOMBLE --");
        log.debug("Womble.womble(Womble)");
        if (instance == null)
            {
            log.debug("Womble.instance [" + instance + "]");
            if (womble != null)
                {
                log.debug("Womble.wombled [" + womble + "][" + womble.getClass() + "]");
                instance = womble ;
                }
            else {
                log.debug("Womble.null :-(");
                }
            }
        else {
            log.debug("Womble.instance [" + instance + "][" + instance.getClass() + "]");
            log.debug("Womble.done :-)");
            }
        }

    public Womble()
        {
        log.debug("-- WOMBLE --");
        log.debug("Womble()");
        }

    /**
     * Our Spring ApplicationContext.
     * 
     */
    @Autowired
    protected ApplicationContext context ;
    public ApplicationContext context()
        {
        return this.context ;
        }

/*
    @Override
    public void afterPropertiesSet()
        {
        log.debug("-- WOMBLE --");
        log.debug("Womble.afterPropertiesSet()");
        womble(
            (Womble) context.getBean(
                Womble.class
                )
            );
        }
 */

    @PostConstruct
    public void postConstruct()
        {
        //log.debug("-- WOMBLE --");
        //log.debug("Womble.postConstruct()");
        womble(
            (Womble) context.getBean(
                Womble.class
                )
            );
        }

    /**
     * Our Account factory.
     * 
     */
    @Autowired
    protected Account.Factory accounts ;
    public Account.Factory accounts()
        {
        return this.accounts ;
        }

    /**
     * Our Job factory.
     * 
     */
    @Autowired
    protected Job.Factory jobs ;
    public Job.Factory jobs()
        {
        return this.jobs ;
        }

    /**
     * Our Tag factory.
     * 
     */
    @Autowired
    protected Tag.Factory tags ;
    public Tag.Factory tags()
        {
        return this.tags ;
        }

    /**
     * Our TapService factory.
     * 
     */
    @Autowired
    protected TapService.Factory services ;
    public TapService.Factory services()
        {
        return this.services ;
        }

    /**
     * Our TapTable factory.
     * 
     */
    @Autowired
    protected TapTable.Factory tables ;
    public TapTable.Factory tables()
        {
        return this.tables ;
        }

    /**
     * Our TapColumn factory.
     * 
     */
    @Autowired
    protected TapColumn.Factory columns ;
    public TapColumn.Factory columns()
        {
        return this.columns ;
        }

    /**
     * Our TapRow factory.
     * 
     */
    @Autowired
    protected TapRow.Factory rows ;
    public TapRow.Factory rows()
        {
        return this.rows ;
        }

    /**
     * Our TapJoin factory.
     * 
     */
    @Autowired
    protected TapJoin.Factory joins ;
    public TapJoin.Factory joins()
        {
        return this.joins ;
        }

    /**
     * Our RowTag factory.
     * 
     */
    @Autowired
    protected RowTag.Factory rowtags ;
    public RowTag.Factory rowtags()
        {
        return this.rowtags ;
        }

    /**
     * Our JoinTag factory.
     * 
     */
    @Autowired
    protected JoinTag.Factory jointags ;
    public JoinTag.Factory jointags()
        {
        return this.jointags ;
        }


    /**
     * Our autowired Hibernate SessionFactory.
     *
     */
    @Autowired
    private SessionFactory hibernate;

    /**
     * Inner class to hold StatefullSession state.
     *
     */
    public class StatefullSessionTools
        {
        }

    /**
     * Inner class to hold StatelessSession state.
     *
     */
    public class StatelessSessionTools
        {

        /**
         * Our Hibernate StatelessSession.
         *
         */
        private StatelessSession session ;
        public StatelessSession session()
            {
            return this.session ;
            }

        /**
         * Our Hibernate Transaction.
         *
         */
        private Transaction transaction ;
        protected Transaction transaction()
            {
            return this.transaction ;
            }

        public void begin()
            {
            //log.debug("StatelessSessionTools.begin()");
            if (this.session == null)
                {
                //log.debug("session is null, starting new one");
                this.session = hibernate.openStatelessSession();

                if (this.transaction == null)
                    {
                    //log.debug("transaction is null, starting new one");
                    this.transaction = this.session.beginTransaction();
                    }
                else {
                    log.error("StatelessSessionTools.begin() called in existing transaction");
                    }
                }
            else {
                log.error("StatelessSessionTools.begin() called in existing session");
                }
            }

        public void commit()
            {
            //log.debug("StatelessSessionTools.commit()");
            if (this.transaction == null)
                {
                log.error("StatelessSessionTools.commit() called with null transaction");
                }
            else {
                //log.debug("transaction is active, comitting");
                this.transaction.commit();
                this.transaction = null ;
                this.close();
                }
            }

        public void rollback()
            {
            //log.debug("StatelessSessionTools.rollback()");
            if (this.transaction == null)
                {
                log.error("StatelessSessionTools.rollback() called with null transaction");
                }
            else {
                //log.debug("transaction is active, rollback");
                try {
                    this.transaction.rollback();
                    }
                finally {
                    this.transaction = null ;
                    this.close();
                    }
                }
            }

        public void close()
            {
            //log.debug("StatelessSessionTools.close()");
            if (this.session == null)
                {
                log.error("StatelessSessionTools.close() called with null session");
                }
            else {
                try {
                    this.session.close();
                    }
                finally {
                    this.transaction = null ;
                    this.session = null ;
                    }
                }
            }

        public void done()
            {
            //log.debug("StatelessSessionTools.done()");
            stateless.set(
                null
                );
            }
        }        

    /**
     * Our ThreadLocal StatelessSessionTools.
     *
     */
    private ThreadLocal<StatelessSessionTools> stateless = 
         new ThreadLocal<StatelessSessionTools>()
            {
            };

    public StatelessSessionTools stateless()
        {
        return stateless.get();
        }

    public void startless()
        {
        //log.debug("startless()");
        if (stateless.get() == null)
            {
            //log.debug("stateless is null, starting new one");
            stateless.set(
                new StatelessSessionTools()
                );
            }
        else {
            log.error("StatelessSessionTools.start() called with existing session");
            }
        }

/*
 * Flush changes ..
 *
 * Clear session ..
 *
 * Evict objects from cache ..
 * http://www.basilv.com/psd/blog/2010/avoiding-caching-to-improve-hibernate-performance
 *
 * Use StatelessSession
 * http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/batch.html#batch-statelesssession
 * https://www.owasp.org/index.php/Hibernate-Guidelines#About_StatelessSession
 *
 *
 *
 */


    }
